from django.http import Http404
from rest_framework.response import Response
from rest_framework.request import Request
from rest_framework.decorators import api_view, permission_classes, action
from rest_framework.viewsets import ModelViewSet, ReadOnlyModelViewSet


from utils.exceptions import InvalidPassword
from user.models import UserProfile
from user.serializers import UserSerializer, PermSerializer, GroupSerializer
from django.contrib.auth.models import User, Group, Permission, ContentType, AnonymousUser
# settings里面添加全局配置
from rest_framework.filters import SearchFilter

_exclude_contenttypes = [
    c.id for c in ContentType.objects.filter(model__in=[
        'logentry', 'group', 'permission',
        'contenttype', 'session',
    ])
]

class RoleViewSet(ModelViewSet):
    queryset = Group.objects.all()
    serializer_class = GroupSerializer
    search_fields = ['name']

    @action(['GET'], detail=True)
    def perms(self, request, pk):
        # 找到pk对应的数据，序列化返回
        print('~' * 30)
        instance = self.get_object()
        # serializer = self.get_serializer(instance)
        data = GroupSerializer(instance).data
        data['allPerms'] = list(PermViewSet.queryset.values('id', 'name', 'codename'))  # 不分页所有权限
        data['allPerms'] = [{
            'id': p.get('id'), 'name': p.get('name'),
            'op': p.get('codename', '').split('_')[0]
        }
            for p in PermViewSet.queryset.values('id', 'name', 'codename')]  # 不分页所有权限
        print(data, '@@@@@@@@@@@@@')
        return Response(data)


# Permission model类是django内部的，内部已经创建好了
class PermViewSet(ReadOnlyModelViewSet):
    # queryset = Permission.objects.filter(pk__gt=20)
    queryset = Permission.objects.exclude(content_type__in=_exclude_contenttypes)
    serializer_class = PermSerializer
    search_fields = ['name', 'codename']

class UserViewSet(ModelViewSet):  # crud
    queryset = UserProfile.objects.all()
    serializer_class = UserSerializer
    # filter_backends = [filters.SearchFilter] # 单独指定查询filter or-->settings里面添加全局配置
    # permission_classes = [IsAdminUser]
    # search_fields = ['username', 'email']
    search_fields = ['username']  # ?search=xxx 前端没传email就是 username='xx' or email='xx'

    # permission_classes = [IsAdminUser]
    # permission_classes = [IsAuthenticated, DjangoModelPermissions] # request.user
    # permission_classes = [IsAuthenticated, CRUDModelPermissions]
    # filter_backends = []

    @action(['GET'], detail=True, url_path='roles')
    def roles(self, request, pk):  # /users/mgr/1/roles/ get
        user = self.get_object()
        data = self.get_serializer(user).data
        data['roles'] = [u['id'] for u in user.groups.values('id')]  # 容器 {}
        data['allRoles'] = Group.objects.values('id', 'name')  # 容器 {}
        return Response(data)

    @roles.mapping.patch  # 同一个路径不同方法 /users/mgr/1/roles/ patch
    def set_roles(self, request, pk):  # /users/mgr/1/roles/ patch
        user = self.get_object()
        # 序列化器，is_valid
        # roles groups
        groups = request.data.get('groups', [])
        user.groups.set(groups)  # sql语句执行
        return Response()

    # @action(methods=['POST'], detail=True, url_path="setpwd", permission_classes=[IsSuperUser]) # /users/100/setpwd/
    # 使用详情页修改
    #@action(methods=['POST'], detail=True, url_path="setpwd")  # /users/100/setpwd/
    # def setpwd(self, request, pk):  # 已经认证过了
    #     # 修改密码
    #     # 查谁的密码？
    #     # 登录用户是谁，就改谁，比较安全
    #     if int(pk) != request.user.id:
    #         raise Http404
    #     user = self.get_object()  # 详情页用的，一定有pk
    #     # user:UserProfile = request.user # token user_id
    #     print(user.password, user.id, user.is_active, user.is_superuser)
    #     if user.check_password(request.data['oldPassword']):
    #         user.set_password(request.data['password'])
    #         user.save()  #
    #         return Response()
    #     else:
    #         raise InvalidPassword

    # 请自行完成 管理员重置其他用户密码，但是不能重置id为1的密码
    @action(methods=['PATCH'], detail=True, url_path="resetpwd")
    def resetpwd(self, request, pk):  # 已经认证过了
        # 修改密码
        # 查谁的密码？
        # 登录用户是谁，就改谁，比较安全
        Default_password = '123456'
        user = self.get_object()  # 详情页用的，一定有pk
        user.set_password(Default_password)
        user.save()  #
        return Response({"message": "重置成功！"})

    # /users/setpwd 不使用详情页修改，即谁登陆修改谁的。
    # token里面带userid,经过认证后会将User实例返回。即request.user
    @action(methods=['PATCH'], detail=False, url_path="setpwd")
    def setpwd(self, request):  # 已经认证过了
        # 修改密码
        # 查谁的密码？
        # 登录用户是谁，就改谁，比较安全
        user: UserProfile = request.user
        # check_password AbstractBaseUser里面有check_password 来自于 from django.contrib.auth.hashers import check_password
        # 所以不需要在import了
        if user.check_password(request.data['oldPassword']):
            user.set_password(request.data['password'])
            user.save()
            return Response({"message": "密码修改成功！"})
        raise InvalidPassword


    # detail=False =>默认为None, False表示这里不是详情页，无需带pk
    # DRF这里的路由只能做简单 /user /user100
    # 或者你手写自定义action path('userinfo/',UserViewSet.as_view('xxx':'yyy'))。这里DRF帮你做了处理 调装饰器
    @action(['GET'], detail=False, url_path='whoami')  # /users/whoami/,url_path='' 取下面函数名
    def whoami(self, request):  # handler
        # user = request.user # 对吗？有没有可能是匿名用户？由于全局设置要求IsAuthenticated，所以这是对的
        return Response({
            'user': {'id': request.user.id, 'username': request.user.username}
        })

    # 控制无法通过patch修改id为1的管理员信息
    # 先查后改、先查后删除 => get_object来查
    def get_object(self):  # 详情页，users/100/ kwargs{'pk':'100'}
        if self.request.method.lower() != "get":
            pk = self.kwargs.get('pk')
            if pk == 1 or pk == '1':
                print('你好大的胆子，竟然操作管理员')
                raise Http404
        return super().get_object()  # pk

    # update(需要传入校验器所有字段) put和patch, partial_update(仅需要传入部分字段即可) patch
    def partial_update(self, request, *args, **kwargs):
        # 处理提交的数据
        request.data.pop('id', None)
        request.data.pop('username', None)
        request.data.pop('password', None)
        request.data.pop('is_superuser', None)
        return super().partial_update(request, *args, **kwargs)
    # 重写read方法 加上filter条件：get_queryset()、list GET /users/? get_object获取详情页信息 与pk有关
    # def get_queryset(self):
    #     # self.request, self.kwargs(这里获取的是url中的路径匹配值)：path('users/<int:pk>',xx)
    #     qs = super().get_queryset()
    #     username = self.request.query_params.get('username', None)
    #     if username:
    #         # 忽略大小写 like
    #         qs = qs.filter(username__icontains=username)
    #     return qs


# 初始化菜单数据，自定义对象转成字典返回
class MenuItem(dict):
    def __init__(self, id, name, path=None):
        super().__init__()
        self['id'] = id
        self['name'] = name  # super().__setitem__('name', name)
        self['path'] = path
        self['children'] = []

    def append(self, item):
        # self.children.append(item)
        self['children'].append(item)
        return self

    def extend(self, *items):
        self['children'].extend(items)
        return self

    # def __getattr__(self, item):
    #     return self[item]


@api_view(['GET'])
@permission_classes([]) # 无权限要求
# @permission_classes([IsAuthenticated])
# @permission_classes([IsSuperUser])
def menulist_view(request: Request):
    # print('~' * 30)
    # print(request.method, request._request.COOKIES, request.headers)
    # print(request.user, bool(request.user), request.user.is_authenticated)
    # print(request.auth, '~~~') # None不成功
    # print()
    # print('~' * 30)
    # return Response({'view':'menu'})menulist = []
    menulist = []
    if request.user:  # .is_superuser
        # 管理员才可以看到的菜单项
        i1 = MenuItem(1, '用户管理')
        i2 = MenuItem(101, '用户列表', '/users')
        i3 = MenuItem(102, '角色列表', '/users/roles')
        i4 = MenuItem(103, '权限列表', '/users/perms')
        # i1.append(i2).append(i3).append(i4)
        i1.extend(i2, i3, i4)
        menulist.append(i1)
    # 这里的权限仅仅只能控制菜单显示 如果访问/cmdb/citypes 控制不住，还需要结合view的权限控制
    # has_perm对管理员不生效
    if request.user.has_perm('cmdb.can_citype'):
        i5 = MenuItem(2, '资产管理')
        i6 = MenuItem(201, '资产类型列表', '/cmdb/citypes')
        i7 = MenuItem(202, '资产列表', '/cmdb/cis')
        i5.append(i6).append(i7)
        menulist.append(i5)
    return Response({
        'default': "/users",
        'menulist': menulist
    })
